package com.chiancloud.shield.core.persist;

import org.apache.hadoop.fs.*;
import org.apache.hadoop.fs.FileSystem;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;


public class JsonSerDeser<T> {

    private static final Logger log = LoggerFactory.getLogger(JsonSerDeser.class);
    private static final String UTF_8 = "UTF-8";

    private final Class classType;
    private final ObjectMapper mapper;


    public JsonSerDeser(Class classType) {
        this.classType = classType;
        this.mapper = new ObjectMapper();
        mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }


    public T fromJson(String json)
            throws IOException, JsonParseException, JsonMappingException {
        try {
            return (T) (mapper.readValue(json, classType));
        } catch (IOException e) {
            log.error("Exception while parsing json : " + e + "\n" + json, e);
            throw e;
        }
    }


    public T fromFile(File jsonFile)
            throws IOException, JsonParseException, JsonMappingException {
        try {
            return (T) (mapper.readValue(jsonFile, classType));
        } catch (IOException e) {
            log.error("Exception while parsing json file {}: {}", jsonFile, e);
            throw e;
        }
    }


    public T fromResource(String resource)
            throws IOException, JsonParseException, JsonMappingException {
        try (InputStream resStream = this.getClass().getResourceAsStream(resource)) {
            if (resStream == null) {
                throw new FileNotFoundException(resource);
            }
            return (T) (mapper.readValue(resStream, classType));
        } catch (IOException e) {
            log.error("Exception while parsing json resource {}: {}", resource, e);
            throw e;
        }
    }


    public T fromInstance(T instance) throws IOException {
        return fromJson(toJson(instance));
    }


    public T fromBytes(byte[] b) throws IOException {
        String json = new String(b, 0, b.length, UTF_8);
        return fromJson(json);
    }


    public T load(FileSystem fs, Path path)
            throws IOException, JsonParseException, JsonMappingException {
        FileStatus status = fs.getFileStatus(path);
        long len = status.getLen();
        byte[] b = new byte[(int) len];
        FSDataInputStream dataInputStream = fs.open(path);
        int count = dataInputStream.read(b);
        if (count != len) {
            throw new EOFException("Read finished prematurely");
        }
        return fromBytes(b);
    }

    public void save(FileSystem fs, Path path, T instance,
                     boolean overwrite) throws
            IOException {
        FSDataOutputStream dataOutputStream = fs.create(path, overwrite);
        writeJsonAsBytes(instance, dataOutputStream);
    }

    private void writeJsonAsBytes(T instance,
                                  DataOutputStream dataOutputStream) throws
            IOException {
        try {
            String json = toJson(instance);
            byte[] b = json.getBytes(UTF_8);
            dataOutputStream.write(b);
        } finally {
            dataOutputStream.close();
        }
    }


    public String toJson(T instance) throws IOException,
            JsonGenerationException,
            JsonMappingException {
        mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
        return mapper.writeValueAsString(instance);
    }
}
